package org.jeecg.access.platform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.access.constant.PlatformType;
import org.jeecg.access.entity.PlatformAccount;
import org.jeecg.access.platform.constant.DataState;
import org.jeecg.access.platform.entity.*;
import org.jeecg.access.platform.mapper.*;
import org.jeecg.access.platform.service.BaseGetDataService;
import org.jeecg.access.platform.service.PlatformHandler;
import org.jeecg.access.platform.util.HttpRequestUtil;
import org.jeecg.access.platform.util.JsonParserUtils;
import org.jeecg.access.service.PlatformAccountService;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.util.DateTimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信平台数据获取服务实现类
 * 实现BaseGetDataService和PlatformHandler接口，负责微信公众号相关数据的获取与存储
 * 已完全使用HttpRequestUtil替代RestTemplate处理HTTP请求
 */

@Slf4j
@Service
public class WxGetDataServiceImpl implements PlatformHandler, BaseGetDataService {

    // 错误码常量
    private static final int API_UNAUTHORIZED_ERROR = 48001; // 微信API未授权错误码
    private static final int TOKEN_EXPIRATION_BUFFER = 300; // Token过期缓冲时间(秒)
    private static final int MAX_ALLOWED_DAYS_SPAN = 7; // 微信API允许的最大日期跨度(天)
    private static final int DEFAULT_QUERY_DAYS = 7; // 默认查询天数

    // 日期格式常量
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    // Redis key前缀
    private static final String REDIS_TOKEN_KEY_PREFIX = "wechat_token:";

    // 微信API地址配置
    @Value("${wx.getToken}")
    private String tokenUrl; // 获取AccessToken的接口地址
    @Value("${wx.usersummary}")
    private String userSummaryUrl; // 用户摘要数据接口地址
    @Value("${wx.usercumulate}")
    private String userCumulateUrl; // 用户累计数据接口地址
    @Value("${wx.articlesummary}")
    private String articleSummaryUrl; // 图文摘要数据接口地址
    @Value("${wx.articletotal}")
    private String articleTotalUrl; // 图文总数据接口地址

    // 依赖注入
    private final WxLoginMapper loginMapper;
    private final RedisUtil redisUtil;
    private final HttpRequestUtil httpRequestUtil;
    private final WxFansDayMoveDataMapper fansMoveDataMapper;
    private final WxFansDayTotalDataMapper fansTotalMapper;
    private final WxArticleSendDataMapper articleMapper;
    private final WxArticleSendTotalDataMapper articleTotalMapper;

    private final PlatformAccountService platformAccountService;

    // 复用ObjectMapper实例提升性能
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    public WxGetDataServiceImpl(WxLoginMapper loginMapper, RedisUtil redisUtil,
                                HttpRequestUtil httpRequestUtil,
                                WxFansDayMoveDataMapper fansMoveDataMapper,
                                WxFansDayTotalDataMapper fansTotalMapper,
                                WxArticleSendDataMapper articleMapper,
                                WxArticleSendTotalDataMapper articleTotalMapper,
                                PlatformAccountService platformAccountService) {
        this.loginMapper = loginMapper;
        this.redisUtil = redisUtil;
        this.httpRequestUtil = httpRequestUtil;
        this.fansMoveDataMapper = fansMoveDataMapper;
        this.fansTotalMapper = fansTotalMapper;
        this.articleMapper = articleMapper;
        this.articleTotalMapper = articleTotalMapper;
        this.platformAccountService = platformAccountService;
    }

    /**
     * 实现接口方法：获取并存储微信AccessToken
     */
    @Override
    public void saveUserInfo() {
        log.info("===== 开始执行微信AccessToken获取逻辑 =====");
        List<WxLoginInfo> accounts = getValidAccounts();
        if (accounts.isEmpty()) {
            log.info("未找到有效公众号配置，无需处理AccessToken");
            log.info("===== 微信AccessToken获取逻辑执行完成 =====");
            return;
        }

        log.info("共找到{}个有效公众号，开始逐个处理", accounts.size());
        for (int i = 0; i < accounts.size(); i++) {
            WxLoginInfo account = accounts.get(i);
            try {
                log.info("开始处理公众号[{}]的AccessToken", account.getAppId());
                String redisKey = REDIS_TOKEN_KEY_PREFIX + account.getAppId();
                String cachedToken = (String) redisUtil.get(redisKey);

                if (cachedToken != null) {
                    log.debug("公众号[{}]的AccessToken已存在于Redis，无需重新获取", account.getAppId());
                    continue;
                }

                log.info("公众号[{}]的AccessToken不存在于Redis，开始调用接口获取", account.getAppId());
                String newToken = fetchNewAccessToken(account);

                int expireSeconds = 7200 - TOKEN_EXPIRATION_BUFFER;
                redisUtil.set(redisKey, newToken, expireSeconds);
                log.info("公众号[{}]的AccessToken已存入Redis，有效期{}秒", account.getAppId(), expireSeconds);

            } catch (WeChatAPIException e) {
                handleWeChatAPIException(e, account, accounts, i);
            } catch (Exception e) {
                log.error("公众号[{}]的AccessToken处理失败: {}", account.getAppId(), e.getMessage(), e);
            }
        }
        log.info("===== 微信AccessToken获取逻辑执行完成 =====");
    }

    /**
     * 实现接口方法：保存微信各类数据
     */
    @Override
    public void saveWorksData(Integer dataNum) {
        log.info("===== 开始执行微信数据保存逻辑 =====");
        int daysRange = (dataNum == null || dataNum <= 0) ? DEFAULT_QUERY_DAYS : Math.min(dataNum, MAX_ALLOWED_DAYS_SPAN);
        log.info("实际使用的日期查询跨度: {}天（最大限制: {}天）", daysRange, MAX_ALLOWED_DAYS_SPAN);

        List<WxLoginInfo> accounts = getValidAccounts();
        if (accounts.isEmpty()) {
            log.info("未找到有效公众号配置，无需保存数据");
            log.info("===== 微信数据保存逻辑执行完成 =====");
            return;
        }

        log.info("共找到{}个有效公众号，开始逐个处理数据", accounts.size());
        for (int i = 0; i < accounts.size(); i++) {
            WxLoginInfo account = accounts.get(i);
            try {
                log.info("开始处理公众号[{}]的数据", account.getAppId());
                String token = getAccessTokenFromRedis(account);
                if (token == null) {
                    log.warn("公众号[{}]未获取到有效的AccessToken，跳过数据处理", account.getAppId());
                    continue;
                }

                // 处理各类数据
                processUserCumulate(account, token, daysRange);
                processUserSummary(account, token, daysRange);
                processArticleSummary(account, token);
                processArticleTotal(account, token);

            } catch (WeChatAPIException e) {
                handleWeChatAPIException(e, account, accounts, i);
            } catch (Exception e) {
                log.error("公众号[{}]的数据保存失败: {}", account.getAppId(), e.getMessage(), e);
            }
        }
        log.info("===== 微信数据保存逻辑执行完成 =====");
    }

    // ======================== 核心数据处理方法 ======================== //

    /**
     * 处理用户累计数据
     */
    private void processUserCumulate(WxLoginInfo account, String token, int daysRange) throws Exception {
        log.info("----- 开始处理公众号[{}]的用户累计数据 -----", account.getAppId());
        String[] dateRange = getValidDateRange(daysRange);
        log.info("用户累计数据查询日期范围: {} 至 {}", dateRange[0], dateRange[1]);

        // 调用API获取响应字符串
        String responseStr = fetchWeChatData(
                token,
                userCumulateUrl,
                new DateRangeRequest(dateRange[0], dateRange[1]),
                "用户累计数据"
        );

        // 提取数据列表（用户累计数据在"list"节点下）
        List<WxFansDayTotalData> dataList = extractUserCumulateData(responseStr, account);
        log.info("公众号[{}]获取到用户累计数据{}条", account.getAppId(), dataList.size());

        if (dataList.isEmpty()) {
            log.info("公众号[{}]未查询到用户累计数据，无需保存", account.getAppId());
            log.info("----- 公众号[{}]的用户累计数据处理完成 -----", account.getAppId());
            return;
        }

        prepareAndSaveFansTotalData(dataList, account);
        log.info("----- 公众号[{}]的用户累计数据处理完成 -----", account.getAppId());
    }

    /**
     * 处理用户摘要数据
     */
    private void processUserSummary(WxLoginInfo account, String token, int daysRange) throws Exception {
        log.info("----- 开始处理公众号[{}]的用户摘要数据 -----", account.getAppId());
        String[] dateRange = getValidDateRange(daysRange + 1);
        log.info("用户摘要数据查询日期范围: {} 至 {}", dateRange[0], dateRange[1]);

        String responseStr = fetchWeChatData(
                token,
                userSummaryUrl,
                new DateRangeRequest(dateRange[0], dateRange[1]),
                "用户摘要数据"
        );

        // 提取数据列表（用户摘要数据在"list"节点下）
        List<WxFansDayMoveData> dataList = extractUserSummaryData(responseStr, account);
        log.info("公众号[{}]获取到用户摘要数据{}条", account.getAppId(), dataList.size());

        if (dataList.isEmpty()) {
            log.info("公众号[{}]未查询到用户摘要数据，无需保存", account.getAppId());
            log.info("----- 公众号[{}]的用户摘要数据处理完成 -----", account.getAppId());
            return;
        }

        prepareAndSaveFansMoveData(dataList, account);
        log.info("----- 公众号[{}]的用户摘要数据处理完成 -----", account.getAppId());
    }

    /**
     * 处理图文摘要数据
     */
    private void processArticleSummary(WxLoginInfo account, String token) throws Exception {
        log.info("----- 开始处理公众号[{}]的图文摘要数据 -----", account.getAppId());
        String yesterday = getYesterday();
        log.info("图文摘要数据查询日期: {}", yesterday);

        String responseStr = fetchWeChatData(
                token,
                articleSummaryUrl,
                new DateRangeRequest(yesterday, yesterday),
                "图文摘要数据"
        );

        // 提取数据列表（图文摘要数据在"list"节点下）
        List<WxArticleSendData> dataList = extractArticleSummaryData(responseStr, account);
        log.info("公众号[{}]获取到图文摘要数据{}条", account.getAppId(), dataList.size());

        if (dataList.isEmpty()) {
            log.info("公众号[{}]未查询到图文摘要数据，无需保存", account.getAppId());
            log.info("----- 公众号[{}]的图文摘要数据处理完成 -----", account.getAppId());
            return;
        }

        prepareAndSaveArticleData(dataList, account);
        log.info("----- 公众号[{}]的图文摘要数据处理完成 -----", account.getAppId());
    }

    /**
     * 处理图文总数据
     */
    private void processArticleTotal(WxLoginInfo account, String token) throws Exception {
        log.info("----- 开始处理公众号[{}]的图文总数据 -----", account.getAppId());
        String yesterday = getYesterday();
        log.info("图文总数据查询日期: {}", yesterday);

        String responseStr = fetchWeChatData(
                token,
                articleTotalUrl,
                new DateRangeRequest(yesterday, yesterday),
                "图文总数据"
        );

        // 提取数据列表（图文总数据在"list"节点下的"items"中）
        List<WxArticleSendTotalData> dataList = extractArticleTotalData(responseStr, account);
        log.info("公众号[{}]获取到图文总数据{}条", account.getAppId(), dataList.size());

        if (!dataList.isEmpty()) {
            articleTotalMapper.batchUpSert(dataList);
            log.info("公众号[{}]成功保存{}条图文总数据", account.getAppId(), dataList.size());
        }
        log.info("----- 公众号[{}]的图文总数据处理完成 -----", account.getAppId());
    }

    // ======================== 辅助方法：数据提取（基于JsonParserUtils） ======================== //

    /**
     * 从响应字符串提取用户累计数据
     */
    private List<WxFansDayTotalData> extractUserCumulateData(String responseStr, WxLoginInfo account) {
        List<WxFansDayTotalData> dataList = new ArrayList<>();
        // 获取"list"数组长度
        Integer listLength = JsonParserUtils.getListLength(responseStr, "list");
        if (listLength == null || listLength <= 0) {
            return dataList;
        }

        // 遍历数组提取每个元素
        for (int i = 0; i < listLength; i++) {
            WxFansDayTotalData data = new WxFansDayTotalData();
            // 提取日期（list[i].ref_date）
            data.setDate(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].ref_date"));
            data.setUserSource(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].user_source"));
            // 提取累计用户数（list[i].cumulate_user）
            data.setCumulateUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].cumulate_user"));
            dataList.add(data);
        }
        return dataList;
    }

    /**
     * 从响应字符串提取用户摘要数据
     */
    private List<WxFansDayMoveData> extractUserSummaryData(String responseStr, WxLoginInfo account) {
        List<WxFansDayMoveData> dataList = new ArrayList<>();
        Integer listLength = JsonParserUtils.getListLength(responseStr, "list");
        if (listLength == null || listLength <= 0) {
            return dataList;
        }

        for (int i = 0; i < listLength; i++) {
            WxFansDayMoveData data = new WxFansDayMoveData();
            data.setDate(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].ref_date"));
            data.setUserSource(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].user_source"));
            data.setNewUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].new_user"));
            data.setCancelUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].cancel_user"));
            dataList.add(data);
        }
        return dataList;
    }

    /**
     * 从响应字符串提取图文摘要数据
     */
    private List<WxArticleSendData> extractArticleSummaryData(String responseStr, WxLoginInfo account) {
        List<WxArticleSendData> dataList = new ArrayList<>();
        Integer listLength = JsonParserUtils.getListLength(responseStr, "list");
        if (listLength == null || listLength <= 0) {
            return dataList;
        }

        for (int i = 0; i < listLength; i++) {
            WxArticleSendData data = new WxArticleSendData();
            data.setDate(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].ref_date"));
            data.setMsgId(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].msgid"));
            data.setTitle(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].title"));
            data.setUserSource(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].user_source"));
            data.setPageReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].int_page_read_user"));
            data.setPageReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].int_page_read_count"));
            data.setOriPageReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].ori_page_read_user"));
            data.setOriPageReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].ori_page_read_count"));
            data.setShareUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].share_user"));
            data.setShareCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].share_count"));
            data.setFavUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].add_to_fav_user"));
            data.setFavCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].add_to_fav_count"));

            dataList.add(data);
        }
        return dataList;
    }

    /**
     * 从响应字符串提取图文总数据
     */
    private List<WxArticleSendTotalData> extractArticleTotalData(String responseStr, WxLoginInfo account) {
        List<WxArticleSendTotalData> dataList = new ArrayList<>();
        // 图文总数据结构：list -> items -> details
        Integer listLength = JsonParserUtils.getListLength(responseStr, "list");
        if (listLength == null || listLength <= 0) {
            return dataList;
        }

        String currentTimestamp = DateTimeUtil.getCurrentTimestamp();
        for (int i = 0; i < listLength; i++) {
            // 获取单条图文的基本信息
            String msgId = JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].msgid");
            String title = JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].title");
            Integer source = JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].user_source");

            // 获取details数组长度
            Integer detailsLength = JsonParserUtils.getListLength(responseStr, "list[" + i + "].details");
            if (detailsLength == null || detailsLength <= 0) {
                continue;
            }

            // 提取details中的数据
            for (int j = 0; j < detailsLength; j++) {
                WxArticleSendTotalData detail = new WxArticleSendTotalData();
                detail.setId(UUIDGenerator.generate());
                detail.setLoginId(account.getId());
                detail.setQueryDate(currentTimestamp);
                detail.setMsgId(msgId);
                detail.setTitle(title);
                detail.setUserSource(source);

                detail.setDate(JsonParserUtils.getStringValueFromString(responseStr, "list[" + i + "].details[" + j + "].stat_date"));
                detail.setTargetUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].target_user"));
                detail.setPageReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_read_user"));
                detail.setPageReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_read_count"));
                detail.setOriPageReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].ori_page_read_user"));
                detail.setOriPageReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].ori_page_read_count"));
                detail.setShareUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].share_user"));
                detail.setShareCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].share_count"));
                detail.setFavUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].add_to_fav_user"));
                detail.setFavCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].add_to_fav_count"));
                detail.setSessionReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_session_read_user"));
                detail.setSessionReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_session_read_count"));
                detail.setHisMsgReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_hist_msg_read_user"));
                detail.setHisMsgReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_hist_msg_read_count"));
                detail.setFeedReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_feed_read_user"));
                detail.setFeedReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_feed_read_count"));
                detail.setFriendsReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_friends_read_user"));
                detail.setFriendsReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_friends_read_count"));
                detail.setOtherReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_other_read_user"));
                detail.setOtherReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_other_read_count"));
                detail.setFeedShareSessionUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_session_user"));
                detail.setFeedShareSessionCnt(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_session_cnt"));
                detail.setFeedShareFeedUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_feed_user"));
                detail.setFeedShareFeedCnt(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_feed_cnt"));
                detail.setFeedShareOtherUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_other_user"));
                detail.setFeedShareOtherCnt(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].feed_share_from_other_cnt"));
                detail.setKanyikanReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_kanyikan_read_user"));
                detail.setKanyikanReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_kanyikan_read_count"));
                detail.setSouyisouReadUser(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_souyisou_read_user"));
                detail.setSouyisouReadCount(JsonParserUtils.getIntValueFromString(responseStr, "list[" + i + "].details[" + j + "].int_page_from_souyisou_read_count"));
                dataList.add(detail);
            }
        }
        return dataList;
    }

    // ======================== 辅助方法：Token与API调用 ======================== //

    private String getAccessTokenFromRedis(WxLoginInfo account) {
        String redisKey = REDIS_TOKEN_KEY_PREFIX + account.getAppId();
        return (String) redisUtil.get(redisKey);
    }

    /**
     * 调用微信接口获取新的AccessToken（使用JsonParserUtils解析）
     */
    private String fetchNewAccessToken(WxLoginInfo account) throws Exception {
        String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
                tokenUrl, account.getAppId(), account.getSecret());

        log.debug("调用微信AccessToken接口: {}", maskSensitiveInfo(url));

        Map<String, String> headers = new HashMap<>();
        String responseStr = httpRequestUtil.sendRequest(url, "GET", headers, null, new HttpRequestUtil.RetryDecision() {
            @Override
            public boolean needRetry(int responseCode, String response) {
                if (responseCode != 200) {
                    return true;
                }
                // 检查是否有错误（有errcode且不为0）
                Integer errCode = JsonParserUtils.getIntValueFromString(response, "errcode");
                return errCode != null && errCode != 0;
            }
        });

        // 记录原始响应报文
        log.info("微信AccessToken接口返回报文:\n{}", responseStr);

        if (responseStr == null || responseStr.isEmpty()) {
            throw new WeChatAPIException(-1, "获取Token响应为空");
        }

        // 双重判断：检查错误码，同时检查是否有token
        Integer errCode = JsonParserUtils.getIntValueFromString(responseStr, "errcode");
        String errMsg = JsonParserUtils.getStringValueFromString(responseStr, "errmsg");
        if (errCode != null && errCode != 0) {
            throw new WeChatAPIException(errCode, String.format("获取Token失败[%d]: %s", errCode, errMsg));
        }

        String token = JsonParserUtils.getStringValueFromString(responseStr, "access_token");
        if (token == null || token.isEmpty()) {
            throw new WeChatAPIException(-1, "响应中未包含有效的access_token");
        }

        return token;
    }

    /**
     * 调用微信API获取数据（返回响应字符串，使用JsonParserUtils解析）
     */
    private String fetchWeChatData(
            String token,
            String baseUrl,
            DateRangeRequest requestBody,
            String dataType
    ) throws Exception {
        String url = baseUrl + "?access_token=" + token;
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", MediaType.APPLICATION_JSON_VALUE);

        // 请求体转换为JSON
        String requestBodyJson = objectMapper.writeValueAsString(requestBody);
        log.info("微信{}API请求参数: {}", dataType, requestBodyJson);
        log.debug("调用微信{}API地址: {}", dataType, maskSensitiveInfo(url));

        String responseStr = httpRequestUtil.sendRequest(url, "POST", headers, requestBodyJson, new HttpRequestUtil.RetryDecision() {
            @Override
            public boolean needRetry(int responseCode, String response) {
                if (responseCode != 200) {
                    return true;
                }
                // 检查是否有错误码
                Integer errCode = JsonParserUtils.getIntValueFromString(response, "errcode");
                return errCode != null && errCode != 0;
            }
        });

        // 记录原始响应报文
        log.info("微信{}API返回报文:\n{}", dataType, responseStr);

        if (responseStr == null || responseStr.isEmpty()) {
            throw new WeChatAPIException(-1, "微信API返回空响应");
        }

        // 双重判断：检查错误码，同时检查是否有数据节点（不同接口数据节点可能为"list"）
        Integer errCode = JsonParserUtils.getIntValueFromString(responseStr, "errcode");
        String errMsg = JsonParserUtils.getStringValueFromString(responseStr, "errmsg");
        boolean hasData = JsonParserUtils.isNodeValid(responseStr, "list"); // 多数接口数据在"list"节点

        if (errCode != null && errCode != 0) {
            throw new WeChatAPIException(errCode, String.format("微信API错误[%d]: %s", errCode, errMsg));
        }

        if (!hasData) {
            log.warn("微信{}API响应中未找到有效数据节点，但无错误码，视为空数据", dataType);
        }

        return responseStr;
    }

    // ======================== 其他辅助方法 ======================== //

    /**
     * 获取有效的公众号账号列表
     */
    private List<WxLoginInfo> getValidAccounts() {
        List<WxLoginInfo> accounts = loginMapper.getInfoList();
        if (accounts == null) {
            return new ArrayList<>();
        }

        List<WxLoginInfo> validAccounts = new ArrayList<>(accounts.size()); // 预设容量提升性能
        for (int i = 0; i < accounts.size(); i++) {
            WxLoginInfo account = accounts.get(i);
            if (account.getState() == DataState.INIT) {
                validAccounts.add(account);
            }
        }

        log.debug("获取到有效公众号{}个（总查询到{}个）", validAccounts.size(), accounts.size());
        return validAccounts;
    }

    /**
     * 计算有效的日期范围（确保不超过微信API限制）
     */
    private String[] getValidDateRange(int requestedDaysSpan) {
        int daysSpan = Math.min(requestedDaysSpan, MAX_ALLOWED_DAYS_SPAN);
        String endDate = getYesterday();
        String startDate = getPastDate(daysSpan);

        LocalDate start = LocalDate.parse(startDate, DATE_FORMATTER);
        LocalDate end = LocalDate.parse(endDate, DATE_FORMATTER);
        long actualDaysSpan = end.toEpochDay() - start.toEpochDay() + 1;

        if (actualDaysSpan > MAX_ALLOWED_DAYS_SPAN) {
            log.warn("请求的日期范围 {} 至 {} 跨度{}天，超过最大限制{}天，已自动调整",
                    startDate, endDate, actualDaysSpan, MAX_ALLOWED_DAYS_SPAN);
            startDate = end.minusDays(MAX_ALLOWED_DAYS_SPAN - 1).format(DATE_FORMATTER);
        }

        return new String[]{startDate, endDate};
    }

    /**
     * 准备并保存用户累计数据
     */
    private void prepareAndSaveFansTotalData(List<WxFansDayTotalData> dataList, WxLoginInfo account) {
        String currentTimestamp = DateTimeUtil.getCurrentTimestamp();
        for (int i = 0; i < dataList.size(); i++) {
            WxFansDayTotalData data = dataList.get(i);
            data.setId(UUIDGenerator.generate());
            data.setLoginId(account.getId());
            data.setQueryDate(currentTimestamp);
        }
        fansTotalMapper.batchUpSert(dataList);
        log.info("公众号[{}]成功保存{}条用户累计数据", account.getAppId(), dataList.size());
    }

    /**
     * 准备并保存图文摘要数据
     */
    private void prepareAndSaveArticleData(List<WxArticleSendData> dataList, WxLoginInfo account) {
        String currentTimestamp = DateTimeUtil.getCurrentTimestamp();
        for (int i = 0; i < dataList.size(); i++) {
            WxArticleSendData data = dataList.get(i);
            data.setId(UUIDGenerator.generate());
            data.setLoginId(account.getId());
            data.setQueryDate(currentTimestamp);
        }
        articleMapper.batchUpSert(dataList);
        log.info("公众号[{}]成功保存{}条图文摘要数据", account.getAppId(), dataList.size());
    }

    /**
     * 准备并保存用户摘要数据
     */
    private void prepareAndSaveFansMoveData(List<WxFansDayMoveData> dataList, WxLoginInfo account) {
        String currentTimestamp = DateTimeUtil.getCurrentTimestamp();
        for (int i = 0; i < dataList.size(); i++) {
            WxFansDayMoveData data = dataList.get(i);
            data.setId(UUIDGenerator.generate());
            data.setLoginId(account.getId());
            data.setQueryDate(currentTimestamp);
        }
        fansMoveDataMapper.batchUpSert(dataList);
        log.info("公众号[{}]成功保存{}条用户摘要数据", account.getAppId(), dataList.size());
    }

    /**
     * 处理微信API异常
     */
    private void handleWeChatAPIException(WeChatAPIException e, WxLoginInfo account, List<WxLoginInfo> accounts, int index) {
        if (e.getErrorCode() == API_UNAUTHORIZED_ERROR) {
            log.error("公众号[{}]授权失效: {}", account.getAppId(), e.getMessage());
            updateAccountState(account, DataState.WECHAT_AUTHORIZE_FAILED);
            accounts.remove(index);
        } else {
            log.error("公众号[{}]微信API错误[{}]: {}",
                    account.getAppId(), e.getErrorCode(), e.getMessage());
        }
    }

    /**
     * 更新公众号状态
     */
    private void updateAccountState(WxLoginInfo account, int state) {
        try {
            account.setState(state);
            account.setUpdateTime(DateTimeUtil.getCurrentTimestamp());
            loginMapper.updateById(account);
            log.info("公众号[{}]状态已更新为: {}", account.getAppId(), state);

            String redisKey = REDIS_TOKEN_KEY_PREFIX + account.getAppId();
            redisUtil.del(redisKey);
            log.debug("已清除公众号[{}]的失效Token缓存", account.getAppId());
        } catch (Exception e) {
            log.error("更新公众号[{}]状态失败: {}", account.getAppId(), e.getMessage(), e);
        }
    }

    /**
     * 获取昨天的日期字符串
     */
    private String getYesterday() {
        return getPastDate(1);
    }

    /**
     * 获取几天前的日期字符串
     */
    private String getPastDate(int daysAgo) {
        return LocalDate.now().minusDays(daysAgo).format(DATE_FORMATTER);
    }

    /**
     * 掩盖URL中的敏感信息（如appsecret）
     */
    private String maskSensitiveInfo(String url) {
        if (url == null) {
            return null;
        }
        // 掩盖appsecret参数
        return url.replaceAll("secret=([^&]*)", "secret=***");
    }

    // ======================== PlatformHandler接口实现 ======================== //

    @Override
    public String getPlatformType() {
        return PlatformType.WECHAT.getCode();
    }

    @Override
    public List<?> fetchExternalData() {
        List<WxLoginInfo> userList = loginMapper.getInfoList();
        if (userList == null) {
            log.warn("本地微信用户表查询返回null，返回空列表");
            return new ArrayList<>();
        }
        log.info("从本地微信用户表获取到 {} 条已有数据", userList.size());
        return userList;
    }

    @Override
    public String extractAccountId(Object data) {
        if (data == null || !(data instanceof WxLoginInfo)) {
            log.warn("本地用户数据格式无效，无法提取账号ID");
            return null;
        }
        WxLoginInfo localUser = (WxLoginInfo) data;
        return localUser.getId();
    }

    @Override
    public boolean verifyAccount(PlatformAccount account) {
        String appId = account.getAppId();
        log.info("开始验证微信账号有效性：appId={}", appId);

        try {
            String redisKey = REDIS_TOKEN_KEY_PREFIX + appId;
            String cachedToken = (String) redisUtil.get(redisKey);
            if (cachedToken != null) {
                log.info("微信账号验证成功（Redis中存在有效token）：appId={}", appId);
                return true;
            }

            log.info("Redis中无有效token，尝试获取新token：appId={}", appId);

            WxLoginInfo userInfo = new WxLoginInfo();
            userInfo.setAppId(appId);
            userInfo.setSecret(account.getAppSecret());

            String newToken = fetchNewAccessToken(userInfo);

            if (newToken == null || newToken.isEmpty()) {
                log.info("微信账号 {} 获取token失败 ", appId);
                return false;
            }

            int expireSeconds = 7200 - TOKEN_EXPIRATION_BUFFER;
            redisUtil.set(redisKey, newToken, expireSeconds);
            log.info("微信账号验证成功（获取新token并缓存）：appId={}，有效期{}秒", appId, expireSeconds);
            return true;

        } catch (WeChatAPIException e) {
            log.error("微信账号验证失败（API错误）：appId={}，错误码={}，原因={}",
                    appId, e.getErrorCode(), e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("微信账号验证失败（系统异常）：appId={}，原因={}", appId, e.getMessage(), e);
            return false;
        }
    }

    @Override
    public void writeToExternalSystem(PlatformAccount account) {
        log.info("微信录入数据开始");
        if (account == null || account.getAppId() == null || account.getAppId().isEmpty()) {
            log.warn("待同步账号信息无效（ID为空）");
            return;
        }

        String nowTime = DateTimeUtil.getCurrentTimestamp();
        WxLoginInfo userInfo = new WxLoginInfo();

        userInfo.setAppId(account.getAppId());
        userInfo.setName(account.getName());
        userInfo.setSecret(account.getAppSecret());
        userInfo.setCreator(account.getCreator());
        userInfo.setCreateTime(account.getCreateTime());
        userInfo.setUpdater(account.getUpdater());
        userInfo.setUpdateTime(nowTime);
        userInfo.setState(account.getState());
        userInfo.setId(account.getId());

        loginMapper.insert(userInfo);
        log.info("账号[{}]已同步到本地微博用户表", userInfo.getAppId());
    }

    @Override
    public String verifyAddedAccount(PlatformAccount account) {

        if (null == account.getAppId() || account.getAppId().trim().isEmpty()) {
            return "微信账号的appId不能为空";
        }

        if (null == account.getAppSecret() || account.getAppSecret().trim().isEmpty()) {
            return "微信账号的appSecret不能为空";
        }

        QueryWrapper<PlatformAccount> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("platform_type", account.getPlatformType());
        queryWrapper.eq("app_id", account.getAppId());

        int count = platformAccountService.count(queryWrapper);
        if (count > 0) {
            return "微信账号的appId账号已存在";
        }

        return null;
    }

    // ======================== 自定义异常类 ======================== //

    private static class WeChatAPIException extends RuntimeException {
        private final int errorCode;

        public WeChatAPIException(int errorCode, String message) {
            super(message);
            this.errorCode = errorCode;
        }

        public int getErrorCode() {
            return errorCode;
        }
    }

    // ======================== 请求DTO（日期范围） ======================== //

    @Data
    @AllArgsConstructor
    static class DateRangeRequest {
        @JsonProperty("begin_date")
        private String beginDate;

        @JsonProperty("end_date")
        private String endDate;
    }
}